import info.gridworld.actor.Actor;
import info.gridworld.grid.Location;

import java.util.ArrayList;


/**
 * This class implements a single Cell for Conway's Game of Life, a form of cellular automation developed by the British
 * mathematician John Conway.
 * 
 * <p>The AP Computer Science Case Study, GridWorld, forms the framework for this program. This Cell class interacts with others
 * within an ActorWorld.
 * 
 * <p>A Cell can be constructed in the following manner:
 * 
 * <p><code>Cell cell = new Cell();</code>
 * 
 * <p>However, it may be more useful to generate Cells using a loop or with the GridWorld interactive constructors.
 * <p>This Cell implements the following ruleset of the Game of Life:<br>
 * 
 * <p><code>1. Any live cell with fewer than two live neighbors dies, as if caused by underpopulation.
 * <br>2. Any live cell with more than three live neighbors dies, as if by overcrowding.
 * <br>3. Any live cell with two or three live neighbors lives on to the next generation.
 * <br>4. Any dead cell with exactly three live neighbors becomes a live cell.</code>
 * 
 * <p>Experienced users of GridWorld should note that each generation requires two activations of the <code>Step</code> 
 * function. This allows the Game of Life to function as specified within the limitations of the GridWorld program. I
 * apologize for any inconvenience.
 * 
 * <p>This class could certainly be improved by extracting it from the GridWorld framework, as it slows down significantly
 * as the volume of Cells in the Grid increase. A more efficient algorithm may require a departure from the object-oriented
 * paradigm, in order to improve efficiency in traversing the collection of Cells.
 * 
 * @author Drew Gallagher
 * @version 1.0
 * @see GameOfLife
 */
public class Cell extends Actor {
	
	/**
	 * This variable distinguishes the two stages of a Cell's behavior.
	 * <p>True: In the planning stage.
	 * <p>False: In the execution stage.
	 * 
	 * @since 1.0
	 */
	private boolean planStatus;
	
	/**
	 * This variable alerts the execution stage if the Cell is going to die.
	 * 
	 * @since 1.0
	 */
	private boolean awaitsDeath;
	
	/**
	 * A list specifying the neighboring locations in which a new cell will be placed each generation.
	 * 
	 * @since 1.0
	 */
	private ArrayList<Location> newCells;
	
	
	
	/**
	 * Constructs a Cell with the default color (black).
	 *
	 * @since 1.0
	 */
	public Cell() {
		this.setColor(null);
		this.planStatus = true;
		this.awaitsDeath = false;
	} // End Constructor
		
	
	
	/**
	 * Executes the planning stage of a generation.
	 * 
	 * @since 1.0
	 * @see #findAdditions()
	 * @see #shouldDie()
	 */
	private void plan() {
		this.newCells = this.findAdditions();
		this.awaitsDeath = this.shouldDie();
	} // End void plan
	
	/**
	 * Executes the processing/execution stage of a generation.
	 * 
	 * @since 1.0
	 * @see #addCells(ArrayList)
	 */
	private void process() {
		this.addCells(this.newCells);
		if (this.awaitsDeath) {
			this.removeSelfFromGrid();
		} // End If
	} // End void process
		
	
	
	/**
	 * Determines the neighboring Locations into which new Cells should be added in the next generation.
	 * 
	 * @return A collection of Locations.
	 * @since 1.0
	 */
	private ArrayList<Location> findAdditions() {
		ArrayList<Location> locs = this.getGrid().getValidAdjacentLocations(this.getLocation());
		ArrayList<Location> adds = new ArrayList<Location>();
		for (Location loc : locs) {
			if (this.getGrid().getOccupiedAdjacentLocations(loc).size() == 3) {
				adds.add(loc);
			} // End If
		} // End Loop
		return adds;
	} // End ArrayList<Location> findAdditions
	
	/**
	 * Checks if the Cell should be removed in the next generation.
	 * 
	 * @return Longevity of the Cell
	 * @since 1.0
	 */
	private boolean shouldDie() {
		int neighbors = this.getGrid().getOccupiedAdjacentLocations(this.getLocation()).size();
		return (neighbors < 2 || neighbors > 3);
	} // End boolean shouldDie
	
	
	
	/**
	 * Adds Cells into the Locations specified.
	 * 
	 * @param locs Locations into which to add Cells.
	 * @since 1.0
	 */
	private void addCells(ArrayList<Location> locs) {
		for (Location loc : locs) {
			if (this.getGrid().get(loc) == null) {
				new Cell().putSelfInGrid(this.getGrid(), loc);
			} // End If
		} // End Loop
	} // End void process
	
	
	
	/**
	 * Checks the current <code>act()</code> stage, and automatically sets it to the other.
	 * 
	 * @return Current stage.
	 * @since 1.0
	 */
	private boolean isPlanning() {
		return !(this.planStatus = !this.planStatus);
	} // End boolean isPlanning
	
	

	/**
	 * Manages the stages and actions of the Cell object. Overwritten from the Actor class.
	 * 
	 * @since 1.0
	 * @see Actor.#act()
	 */
	public void act() {
		if (this.isPlanning()) {
			this.plan();
		} else {
			this.process();
		} // End If
	} // End void act
	
} // End class Cell
